package com.face.pass.active.controller;

import java.math.BigInteger;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.face.pass.active.App;
import com.face.pass.active.dao.MachineDao;
import com.face.pass.active.dao.SettingDao;
import com.face.pass.active.model.Machine;
import com.face.pass.active.model.Setting;
import com.face.pass.active.utils.DateUtils;
import com.face.pass.active.utils.DeviceUtils;
import com.face.pass.active.utils.LicenseUtils;
import com.face.pass.active.utils.OkHttpUtils;
import com.face.pass.active.view.LoginView;
import com.google.common.base.Strings;

import de.felixroske.jfxsupport.FXMLController;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Dialog;
import javafx.scene.control.Pagination;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.Callback;
import javafx.util.StringConverter;
import okhttp3.HttpUrl;

@FXMLController
public class MainController implements Initializable {

	private static final Logger LOG = LoggerFactory.getLogger(MainController.class);

	@Autowired
	private MachineDao machineDao;
	
	@Autowired
	private SettingDao settingDao;

	@FXML
	private TextField tfMachineId;

	@FXML
	private TableView<Machine> tvResult;

	@FXML
	private TableColumn<Machine, Long> tcId;

	@FXML
	private TableColumn<Machine, String> tcMachineId;

	@FXML
	private TableColumn<Machine, String> tcActive;

	@FXML
	private TableColumn<Machine, Date> tcActiveDate;

	@FXML
	private TableColumn<Machine, String> tcLicenseCode;

	@FXML
	private TableColumn<Machine, String> tcOption;

	@FXML
	private Pagination page;

	@FXML
	private Button btnFind;

	static final int PAGE_SIZE = 12;

	private static DateFormat dfDate = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
	
	private static DateFormat dfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

	@FXML
	private LineChart<String, BigInteger> lcStat;

	@FXML
	private DatePicker dpStartDate;

	@FXML
	private DatePicker dpEndDate;

	@FXML
	private ChoiceBox<String> cbActive;
	
	@FXML
	private CheckBox cbSetIsAuto;
	
	@FXML
	private DatePicker dpSetStartDate;
	
	@FXML
	private DatePicker dpSetEndDate;
	
	private ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		
		tvResult.autosize();
		tcId.setCellValueFactory(new PropertyValueFactory<Machine, Long>("id"));
		tcMachineId.setCellValueFactory(new PropertyValueFactory<Machine, String>("machineId"));
		tcMachineId.setEditable(true);
		tcActiveDate.setCellValueFactory(new PropertyValueFactory<Machine, Date>("activeDate"));
		tcActive.setCellFactory(new Callback<TableColumn<Machine, String>, TableCell<Machine, String>>() {
			@Override
			public TableCell<Machine, String> call(TableColumn<Machine, String> param) {
				return new TableCell<Machine, String>() {

					@Override
					protected void updateItem(String item, boolean empty) {
						super.updateItem(item, empty);
						this.setText(null);
						this.setGraphic(null);
						if (!empty) {
							Machine machine = this.getTableView().getItems().get(this.getIndex());
							if (machine.getActive() == 1) {
								this.setText("已激活");
							} else {
								this.setText("未激活");
							}
						}
					}
				};
			}
		});

		tcLicenseCode.setCellFactory(new Callback<TableColumn<Machine, String>, TableCell<Machine, String>>() {
			@Override
			public TableCell<Machine, String> call(TableColumn<Machine, String> param) {
				return new TableCell<Machine, String>() {

					@Override
					protected void updateItem(String item, boolean empty) {
						super.updateItem(item, empty);
						this.setText(null);
						this.setGraphic(null);
						if (!empty) {
							Machine machine = this.getTableView().getItems().get(this.getIndex());
							if (!Strings.isNullOrEmpty(machine.getLicense())) {
								Button showBtn = new Button("查看");
								this.setGraphic(showBtn);
								showBtn.setOnMouseClicked((me) -> {
									LOG.debug("machine:" + machine);
									TextField tfLicense = new TextField();
									tfLicense.setEditable(false);
									tfLicense.setText(machine.getLicense());
									Alert alert = new Alert(AlertType.INFORMATION);
									alert.setTitle("授权码");
									alert.getDialogPane().setPrefSize(350, 20);
									alert.setHeaderText(null);
									tfLicense.setPrefWidth(350);
									alert.getDialogPane().setContent(tfLicense);
									alert.setHeight(150);
									alert.setGraphic(null);
									alert.setContentText(machine.getLicense());
									alert.show();
								});
							}
						}
					}
				};
			}
		});

		tcOption.setCellFactory((col) -> {
			TableCell<Machine, String> cell = new TableCell<Machine, String>() {

				@Override
				public void updateItem(String item, boolean empty) {
					super.updateItem(item, empty);
					this.setText(null);
					this.setGraphic(null);

					if (!empty) {
						Button activeBtn = new Button("激活");
						activeBtn.setOnMouseClicked((me) -> {
							Machine machine = this.getTableView().getItems().get(this.getIndex());
							try {
								Calendar cal = Calendar.getInstance();
								cal.set(Calendar.DATE, cal.get(Calendar.DATE) -1);
								String license = LicenseUtils.createLicense(machine.getMachineId(), dfDate.parse("2018-10-01"),
										dfDate.parse("2037-01-01"));
								machine.setActiveDate(new Date());
								machine.setLicense(license);
								machine.setUpdateDate(new Date());
								machineDao.saveAndFlush(machine);
								JSONObject req = new JSONObject();
								req.put("license", license);
								req.put("systemTime", dfTime.format(new Date()) );
								JSONObject res = JSON.parseObject(OkHttpUtils.httpPost(
										HttpUrl.get("http://" + machine.getMachineIp() + ":9000/machine/set"),
										req.toJSONString()));
								if (res != null && res.getIntValue("errCode") == 0) {
									machine.setActive(1);
									machineDao.saveAndFlush(machine);
								}
							} catch (Exception e) {
								LOG.debug(" ",e);
							}
							showList(page.getCurrentPageIndex());
						});
						this.setGraphic(activeBtn);
					}
				}
			};
			return cell;
		});

		page.setPageCount((int) machineDao.count() / PAGE_SIZE + (machineDao.count() % PAGE_SIZE != 0 ? 1 : 0));
		page.setPageFactory(new Callback<Integer, Node>() {
			@Override
			public Node call(Integer param) {
				showList(param);
				return tvResult;
			}
		});

		cbActive.setItems(FXCollections.observableArrayList("已激活", "未激活 "));

		StringConverter<LocalDate> converter = new StringConverter<LocalDate>() {
			DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

			@Override
			public String toString(LocalDate date) {
				if (date != null) {
					return dateFormatter.format(date);
				} else {
					return "";
				}
			}

			@Override
			public LocalDate fromString(String string) {
				if (string != null && !string.isEmpty()) {
					return LocalDate.parse(string, dateFormatter);
				} else {
					return null;
				}
			}
		};
		cbActive.getSelectionModel().select(0);
		dpStartDate.setValue(LocalDate.now().minusMonths(6));
		dpEndDate.setValue(LocalDate.parse("2037-01-01"));
		dpStartDate.setConverter(converter);
		dpEndDate.setConverter(converter);
		
		//初始化设置界面
		dpSetStartDate.setValue(LocalDate.now().minusDays(1));
		dpSetEndDate.setValue(LocalDate.parse("2037-01-01"));
		Setting probe = new Setting();
		probe.setKey("isAutoActive");
		Example<Setting> settingExample = Example.of(probe);
		Optional<Setting> settingOp = settingDao.findOne(settingExample);
		if(settingOp.isPresent() && "true".equals(settingOp.get().getValue())) {//已启动自动设置
			cbSetIsAuto.fire();
			cbSetIsAuto.setSelected(true);
		}
	}

	private void showList(int currentPageIndex) {
		ObservableList<Machine> dataList = FXCollections.observableArrayList();
		PageRequest pageable = PageRequest.of(currentPageIndex, PAGE_SIZE, Direction.DESC, "createDate","updateDate","active");
		Iterable<Machine> machines = machineDao.findAll(pageable);
		for (Machine machine : machines) {
			dataList.add(machine);
		}
		tvResult.setItems(dataList);
	}

	@FXML
	public void onAbout(final Event e) {
		Alert alert = new Alert(Alert.AlertType.INFORMATION);
		alert.setTitle("关于");
		alert.setHeaderText(null);
		alert.setContentText("本程序由MR.LUO制作 \t当前版本V1.0");
		alert.initOwner(App.getStage());
		alert.show();
	}

	@FXML
	public void onLogout(final Event e) {
		App.showView(LoginView.class);
	}

	@FXML
	public void onClose(final Event e) {
		App.getStage().close();
	}

	// 检索局域网内的设备
	@FXML
	public void onFind(final Event e) {
		btnFind.setDisable(true);
		List<Machine> machines = DeviceUtils.findDevices();
		for (Machine machine : machines) {
			Machine qMachine = machineDao.findByMachineId(machine.getMachineId());
			if (qMachine != null) {
				qMachine.setMachineIp(machine.getMachineIp());
				machineDao.saveAndFlush(qMachine);
			}else {
				machineDao.saveAndFlush(machine);
			}
		}
		page.setCurrentPageIndex(0);
		showList(0);
		btnFind.setDisable(false);
	}

	// 添加一个授权
	@FXML
	public void onAdd(final Event e) {
		Dialog<Machine> dialog = new Dialog<>();
		dialog.setTitle("新增授权");
		dialog.setResizable(false);
		dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
		TextField tfMachineId = new TextField();
		tfMachineId.setPromptText("设备号");

		Node okButton = dialog.getDialogPane().lookupButton(ButtonType.OK);
		okButton.setDisable(true);
		tfMachineId.textProperty().addListener((observable, oldValue, newValue) -> {
			okButton.setDisable(newValue.trim().isEmpty());
		});
		Platform.runLater(() -> tfMachineId.requestFocus());
		dialog.setResultConverter(new Callback<ButtonType, Machine>() {
			@Override
			public Machine call(ButtonType param) {
				if (param.equals(ButtonType.OK)) {
					String machineId = tfMachineId.getText();
					Machine machine = machineDao.findByMachineId(machineId);
					if (machine != null) {
						return null;
					}
					try {
						Calendar cal = Calendar.getInstance();
						cal.set(Calendar.DATE, cal.get(Calendar.DATE) -1);
						machine = new Machine();
						machine.setMachineId(tfMachineId.getText());
						machine.setActive(1);
						machine.setLicense(LicenseUtils.createLicense(machineId, dfDate.parse("2018-10-01"),dfDate.parse("2037-01-01")));
						machine.setActiveDate(new Date());
						machine.setCreateDate(new Date());
						machine.setUpdateDate(new Date());
					} catch (Exception e) {
						
					}
					return machine;
				}
				return null;
			}
		});
		dialog.getDialogPane().setContent(tfMachineId);
		Optional<Machine> opt = dialog.showAndWait();
		if (opt.isPresent()) {
			machineDao.save(opt.get());
		}
		showList(page.getCurrentPageIndex());
	}

	// 查询结果
	@FXML
	public void onQuery(final Event e) {
		String machineId = tfMachineId.getText();
		if (!Strings.isNullOrEmpty(machineId)) {
			Machine machine = machineDao.findByMachineId(machineId);
			page.setPageCount(1);
			ObservableList<Machine> dataList = FXCollections.observableArrayList();
			if (machine != null) {
				dataList.add(machine);
			}
			tvResult.setItems(dataList);
		} else {
			page.setPageCount((int) machineDao.count() / PAGE_SIZE + (machineDao.count() % PAGE_SIZE != 0 ? 1 : 0));
			showList(0);
		}
	}

	@FXML
	public void onStatQuery(final Event e) {
		Date startDate = DateUtils.localDate2Date(dpStartDate.getValue());
		Date endDate = DateUtils.localDate2Date(dpEndDate.getValue());
		String active = cbActive.getValue();
		if(!Strings.isNullOrEmpty(active)) {
			int activeStat = 0;
			if(active.equals("已激活")) {
				activeStat = 1;
			}
			List<Object[]> list = machineDao.countAllActive(activeStat,startDate,endDate);
			if(list != null) {
				lcStat.getData().clear();
				//defining a series
		        XYChart.Series<String,BigInteger> series = new XYChart.Series<>();
		        series.setName("machine active numbers");
				for(Object[] obj: list) {
					String month = obj[0]+"";
					BigInteger count = (BigInteger)obj[1];
			        series.getData().add(new XYChart.Data<String,BigInteger>(month, count));
				}
				if(!list.isEmpty()) {
					lcStat.getData().add(series);
				}
			}
		}
	}
	
	@FXML
	public void onSetAutoActive(final Event e) {
		if(cbSetIsAuto.isSelected()) {
			Setting setting = new Setting();
			setting.setKey("isAutoActive");
			Optional<Setting> optSetting = settingDao.findOne(Example.of(setting));
			if(optSetting.isPresent()) {
				setting.setValue("true");
				settingDao.saveAndFlush(setting);
			}
			if(pool.isShutdown()) {
				pool = Executors.newScheduledThreadPool(1);
			}
			pool.scheduleAtFixedRate(new Runnable() {
				@Override
				public void run() {
					try {
						//发起检索事件
						btnFind.fire();
						//查找并激活
						Machine machine = new Machine();
						machine.setActive(0);
						Example<Machine> example = Example.of(machine);
						Optional<Machine> _machine = machineDao.findOne(example);
						if(_machine.isPresent()) {
							machine = _machine.get();
							Calendar cal = Calendar.getInstance();
							cal.set(Calendar.DATE, cal.get(Calendar.DATE) -1);
							String license = LicenseUtils.createLicense(machine.getMachineId(), dfDate.parse("2018-10-01"),
									dfDate.parse("2037-01-01"));
							machine.setActiveDate(new Date());
							machine.setLicense(license);
							machine.setUpdateDate(new Date());
							machineDao.saveAndFlush(machine);
							JSONObject req = new JSONObject();
							req.put("license", license);
							req.put("systemTime", dfTime.format(new Date()) );
							JSONObject res = JSON.parseObject(OkHttpUtils.httpPost(
									HttpUrl.get("http://" + machine.getMachineIp() + ":9000/machine/set"),
									req.toJSONString()));
							if (res != null && res.getIntValue("errCode") == 0) {
								machine.setActive(1);
								machineDao.saveAndFlush(machine);
								showList(page.getCurrentPageIndex());
							}
						}
					} catch (Exception e) {
						LOG.debug("设备不在线，激活失败");
					}
				}
			},0,2,TimeUnit.SECONDS);
		}else {
			pool.shutdown();
			Setting setting = new Setting();
			setting.setKey("isActive");
			Optional<Setting> optSetting = settingDao.findOne(Example.of(setting));
			if(optSetting.isPresent()) {
				setting.setValue("false");
				settingDao.saveAndFlush(setting);
			}
		}
	}
}
